package com.golxen.framework.netty.websocket;


import com.golxen.framework.PBPacket.PBPacket;
import com.golxen.framework.common.server.agent.AgentPlayerMgr;
import com.golxen.framework.common.server.agent.AbstractDispatcher;
import com.golxen.framework.netty.websocket.shaker.HandshakerFactory;
import com.golxen.utils.LogUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;

public class AgentWebSocketHandler extends SimpleChannelInboundHandler<Object> {

	private static Logger logger = LogUtil.COMMON;

	private String remoteIp;

	private WebSocketServerHandshaker handshaker;

	public String getRemoteIp() {
		return remoteIp;
	}

	public AgentWebSocketHandler() {
		super();
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		super.channelActive(ctx);
		logger.info("[loginmark]agent channelActive {} {}", ctx.channel().id().asShortText(),
				ctx.channel().remoteAddress());
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		super.channelInactive(ctx);
		logger.info("[loginmark]agent channelInActive {} {}", ctx.channel().id().asShortText(),
				ctx.channel().remoteAddress());
	}

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
		//服务端响应pong帧（心跳）
		if (msg instanceof PongWebSocketFrame) {
			// Pong帧不需要回复
			return;
		}
		//客户端请求ping帧（心跳）
		if (msg instanceof PingWebSocketFrame) {
			byte[] bytes = new byte[2];
			bytes[0] = (byte) 0x8A;
			bytes[1] = (byte) 0x00;
			ByteBuf byteBuf = Unpooled.buffer(2);
			byteBuf.writeBytes(bytes);
			ctx.channel().write(byteBuf);
			return;
		}
		//http 请求（websocket初次建立连接）
		if (msg instanceof FullHttpRequest) {
			http(ctx, (FullHttpRequest) msg);
			return;
		}
		//二进制帧
		if (msg instanceof BinaryWebSocketFrame) {
			websocket(ctx, (BinaryWebSocketFrame) msg);
			return;
		}
		//关闭帧
		if (msg instanceof CloseWebSocketFrame) {
			processCloseWebSocketRequest(ctx, (CloseWebSocketFrame) msg);
		}
	}

	//超时处理
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		super.userEventTriggered(ctx, evt);

		if (evt instanceof IdleStateEvent) {
			IdleStateEvent event = (IdleStateEvent) evt;
			if (event.state() == IdleState.READER_IDLE) {
				Long userId = ctx.channel().attr(AgentWebSocketServer.CLIENT_KEY).get();
				if (userId != null) {
					logger.debug(" idle time out cid={} userId={}", ctx.channel().id().asShortText(), userId);
					AgentPlayerMgr.removeOnline(userId, ctx.channel(), false);
				}
				logger.debug("WebSocket trigger idle event, cid={} userId = {}", ctx.channel().id().asShortText(),
						userId);
			}
		}
	}

	protected void http(ChannelHandlerContext ctx, FullHttpRequest request) {
		if (!HttpMethod.GET.equals(request.method())
				|| !"websocket".equalsIgnoreCase(request.headers().get("Upgrade"))) {
			DefaultHttpResponse resp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST);
			ctx.channel().write(resp);
			ctx.channel().close();
			return;
		}

		HandshakerFactory wsShakerFactory = new HandshakerFactory("ws://" + request.headers().get(HttpHeaderNames.HOST),
				null, false);

		handshaker = wsShakerFactory.newHandshaker(request);

		if (handshaker == null) {
			// 如果不支持WebSocket版本，返回HTTP 405错误
			WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
		} else {
			//升级协议为WebSocket
			handshaker.handshake(ctx.channel(), request);
		}

	}

	protected void websocket(ChannelHandlerContext ctx, BinaryWebSocketFrame frame) {
		PBPacket pkt = new PBPacket();
		pkt.read(frame.content());
		AbstractDispatcher.dispatch(ctx, pkt);
	}

	protected void processCloseWebSocketRequest(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
		Long userId = ctx.channel().attr(AgentWebSocketServer.CLIENT_KEY).get();
		if (userId != null) {
			AgentPlayerMgr.removeOnlineDelay(userId, ctx.channel());
		} else {
			ctx.close();
		}

		logger.info("[loginmark] processCloseWebSocketRequest channelId={} address={}, userId=", //
				ctx.channel().id().asShortText(), //
				ctx.channel().remoteAddress(), //
				userId);
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//		super.exceptionCaught(ctx, cause);

		Long userId = ctx.channel().attr(AgentWebSocketServer.CLIENT_KEY).get();
		if (userId != null) {
			AgentPlayerMgr.removeOnline(userId, ctx.channel(), false);
		}

		logger.warn("[loginmark] exceptionCauth channelId={} address={}, userId={}", //
				ctx.channel().id().asShortText(), //
				ctx.channel().remoteAddress(), //
				userId
		);
		ctx.close();
	}

}
